<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        /* Houdini API */
        @property --x {
            syntax: "<length>";
            initial-value: 0px;
            inherits: false;
        }

        @property --y {
            syntax: "<length>";
            initial-value: 0px;
            inherits: false;
        }

        .container {
            position: relative;

            width: 100%;
            height: 50vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .ball {
            transform: translateX(var(--x)) translateY(var(--y));
            animation: moveX 2s, moveY 1s;
            width: 40px;
            height: 40px;
            background-color: tomato;
            border-radius: 50%;
            animation-fill-mode: forwards;
            /* 开发者自定义的三次贝塞尔曲线, 其中 p1 和 p3 的值必须在 0 到 1 的范围内. */
            /* 
            第一控制点 (0.5, -500): 
                x1=0.5: 动画进行到一半时（时间 50%）, 速度变化达到峰值.
                y1=-500: 关键参数, 表示动画开始后会迅速反向执行（负值）, 且幅度极大（-500）
            第二控制点 (0.5, 500): 
                x2=0.5: 同样在时间 50% 时, 速度变化再次达到峰值.
                y2=500: 关键参数, 表示动画在接近结束时会大幅超过目标值（正值）, 然后回弹.
            */
            animation-timing-function: cubic-bezier(0.5, -500, 0.5, 500);
            animation-iteration-count: infinite;
        }

        @keyframes moveX {
            to {
                --x: 1px;
            }
        }

        @keyframes moveY {
            to {
                --y: 0.5px;
            }
        }

        /* 轨迹画布样式 */
        #trailCanvas {
            position: absolute;
            left: 0;
            top: 0;
            z-index: -1;
            opacity: 1;
        }
    </style>
</head>

<body>
    <div class="container">
        <div class="ball"></div>
        <div><canvas id="trailCanvas"></canvas></div>
    </div>
</body>

<script>
    const ball = document.querySelector('.ball');
    const canvas = document.getElementById('trailCanvas');
    const ctx = canvas.getContext('2d');
    let trailPoints = []; // 存储轨迹点

    // 初始化画布尺寸
    function resizeCanvas() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        ctx.strokeStyle = 'tomato'; // 蓝色轨迹
        ctx.lineWidth = 1;
        ctx.lineCap = 'round';
    }

    // 绘制轨迹点
    function drawTrail() {
        const rect = ball.getBoundingClientRect();
        const x = rect.left + rect.width / 2; // 小球中心点 X
        const y = rect.top + rect.height / 2; // 小球中心点 Y

        trailPoints.push({ x, y });
        if (trailPoints.length > 500) { // 限制轨迹点数, 避免卡顿
            trailPoints.shift();
        }

        ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除旧轨迹（可选）
        ctx.beginPath();
        ctx.moveTo(trailPoints[0].x, trailPoints[0].y);
        trailPoints.forEach(point => {
            ctx.lineTo(point.x, point.y);
        });
        ctx.stroke();
    }

    // 窗口Resize时重置画布
    window.addEventListener('resize', resizeCanvas);
    resizeCanvas(); // 初始化画布

    // 动画帧更新轨迹
    requestAnimationFrame(function animate() {
        drawTrail();
        requestAnimationFrame(animate);
    });
</script>

</html>